Mybatis拦截器与PageHelper一起使用无法拦截分页前的sql解决方案 | 您所在的位置:网站首页 › mybatis pagehelper不生效 › Mybatis拦截器与PageHelper一起使用无法拦截分页前的sql解决方案 |
在使用mybatis拦截器的时候,遇到一个很蛋疼的问题,就是无法拦截pagehelper分页插件执行之前的sql,每次拦截都是已经拼接完sql,而且无法拦截pagehelper中那个select count语句,然而今天我来给大家讲下我是如何解决这个问题的 我在网上查过N篇文章,结果都是直接给出了pagehelper的官方文档,一开始看到别人写的时候还以为这个人这么6的,结果这是官方文档~,这里我也不多说了,先将官网放出来你们有空可以看看 https://github.com/pagehelper/Mybatis-PageHelper/blob/master/wikis/zh/Interceptor.md 通过官方描述我们不难发现,其实我们无法获取分页之前的sql是因为他比我们早一步拦截,所以不管我们怎么获取都是被处理完的sql 所以我们去查看他的源码,通过源码查看不难发现,其实他们主要使用PageInterceptor进行拦截 @Intercepts( { @Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class}), @Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class, CacheKey.class, BoundSql.class}), } ) public class PageInterceptor implements Interceptor {由上述代码我们可以看到,他是拦截了Executor这个类的query方法,并且拦截了4个参数或者6个参数,具体参看上面官方文档 由于mybatis拦截器的顺序是先拦截4个参数的方法,然后在到6个参数的方法,所以官方文档有这么一个描述 所以我们就得直接拦截四个参数的方法,然后再跳到PageInterceptor,这样我们就能先拿到sql了,所以我们来尝试一下 @Intercepts( { @Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class}), } ) @Component public class PowerInterceptor implements Interceptor { @Override public Object intercept(Invocation invocation) throws Throwable { Object[] args = invocation.getArgs(); MappedStatement ms = (MappedStatement) args[0]; Object parameter = args[1]; RowBounds rowBounds = (RowBounds) args[2]; ResultHandler resultHandler = (ResultHandler) args[3]; Executor executor = (Executor) invocation.getTarget(); CacheKey cacheKey; BoundSql boundSql; //由于逻辑关系,只会进入一次 if(args.length == 4){ //4 个参数时 boundSql = ms.getBoundSql(parameter); cacheKey = executor.createCacheKey(ms, parameter, rowBounds, boundSql); } else { //6 个参数时 cacheKey = (CacheKey) args[4]; boundSql = (BoundSql) args[5]; } //TODO 自己要进行的各种处理 //注:下面的方法可以根据自己的逻辑调用多次,在分页插件中,count 和 page 各调用了一次 return executor.query(ms, parameter, rowBounds, resultHandler, cacheKey, boundSql); } @Override public Object plugin(Object target) { return Plugin.wrap(target, this); } @Override public void setProperties(Properties properties) { } }首先我建了一个类叫PowerInterceptor,里面的实现方法和文档中的QueryInterceptor中的代码一样,但是我们拦截了4个参数的方法,然后测试发现,还是不得,他是先执行QueryInterceptor>PageInterceptor,然后这个PowerInterceptor的拦截直接跳过了?因为QueryInterceptor直接执行了下面这句话,导致他不会再次进入四个参数的拦截,然后只能拦截六个参数的方法? executor.query(ms, parameter, rowBounds, resultHandler, cacheKey, boundSql); 然后我再看了一下文档,喔,原来是要配置xml的,好,我配! 毕竟我们spring boot用惯了就很少用过xml来配置了,所以今天来写下吧 在application.properties或者application.yml文件中加入,下面代码为properties文件 mybatis.config-location=classpath:mybatis-config.xml然后在resource目录新建mybatis-config.xml,添加以下内容 DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd">看到这,是不是觉得只要我们PowerInterceptor优先于QueryInterceptor就行了,至于为什么配置是在QueryInterceptor下一行,还是那句话,先看文档。。。 其实我一开始也是以为这就行了,结果mmp,什么都没改变,还是先执行QueryInterceptor>PageInterceptor,PageInterceptor不执行,还是只有拦截六个参数的方法的时候才能拦截,顺序还是在PageInterceptor后 emmm,这鬼东西太烦人了,然后经过一番思考,那我能不能将PageInterceptor也配上,让他在PowerInterceptor之后执行,好吧既然想了就来试试 感觉思路很正常,没错这是个要大成的征兆,是吧 结果,就是我们程序员的日常,“感觉”自己的思路完全可行,然后出现以下代码 Caused by: java.lang.RuntimeException: 在系统中发现了多个分页插件,请检查系统配置!我淦! 意思是PageInterceptor被配置了,如果我们再配置就变多个了 此处1w个草泥马奔腾而过,这该如何是好 最终我做了一个决定,将pagehelper抽出来放在自己的程序上配置,而不是在依赖中,我们先把pagehelper的依赖去掉,然后点击下面链接 在github上拿到pagehelper源码,然后将pagehelper全部源码迁进自己项目的包下 然后添加部分依赖 com.github.jsqlparser jsqlparser 3.2 com.google.guava guava 19.0 compile truemybatis-config.xml的配置不变,然后断点测试 我曹,好像行了 然后修改一下PowerInterceptor的拦截。在args.length == 4也就是拦截四个方法的时候修改sql,ReflectUtil在另一章节已给出, 现在测试一条数据拦截 @Override public Object intercept(Invocation invocation) throws Throwable { Object[] args = invocation.getArgs(); MappedStatement ms = (MappedStatement) args[0]; Object parameter = args[1]; RowBounds rowBounds = (RowBounds) args[2]; ResultHandler resultHandler = (ResultHandler) args[3]; Executor executor = (Executor) invocation.getTarget(); CacheKey cacheKey; BoundSql boundSql; //由于逻辑关系,只会进入一次 if(args.length == 4){ //4 个参数时 boundSql = ms.getBoundSql(parameter); //测试拦截 String sql=boundSql.getSql(); sql="select * from ("+sql + ") as p where p.Id = '22c66bebfa2445aa99965df7e3e5b318'"; ReflectUtil.setFieldValue(boundSql, "sql", sql); cacheKey = executor.createCacheKey(ms, parameter, rowBounds, boundSql); } else { //6 个参数时 cacheKey = (CacheKey) args[4]; boundSql = (BoundSql) args[5]; } //TODO 自己要进行的各种处理 //注:下面的方法可以根据自己的逻辑调用多次,在分页插件中,count 和 page 各调用了一次 return executor.query(ms, parameter, rowBounds, resultHandler, cacheKey, boundSql); }然后测试结果 到这说明已经拦截成功了,在分页之前修改了sql,然后通过分页插件执行了select count和limit 但是这个方法有点笨,毕竟要将pagehelper源码拉过来,虽然不是很多,其实如果能解决那个PageInterceptor加载就不用将代码拉过来了,如果有人可以用其他办法解决请私聊我,我也想知道 解决方法在启动类上加上 @SpringBootApplication(exclude = {PageHelperAutoConfiguration.class}) ps:评论的小伙伴告知可以使用这个方法,暂无时间测试,各位可以自行测试 ReflectUtil工具类和数据权限拦截的具体实现请点击下面链接 springboot mybatis拦截器实现数据权限控制 |
CopyRight 2018-2019 实验室设备网 版权所有 |